package hello.word.m2.maven.support;

import com.google.common.collect.Lists;
//import lombok.EqualsAndHashCode;
//import lombok.Getter;
//import lombok.NonNull;
//import lombok.ToString;

import javax.annotation.Nullable;
import java.util.*;

public interface Entity<ID> {

	ID id();

	Object getFieldValue(/*@NonNull*/ String fieldOrColumnName);

	void setField(/*@NonNull*/ String fieldOrColumnNam, String value);

	String getFieldValueAsString(/*@NonNull*/ String fieldOrColumnName);

	boolean hasField(/*@NonNull*/ String fieldOrColumnName);

	KeyValueMap allFields();

//	@EqualsAndHashCode(of = "sourceMap")
//	@ToString(of = "sourceMap")
	final class KeyValueMap {
//		@Getter
		private final Map<String, Object> sourceMap;
		private final List<String> keys;
		private final List<Object> values;

		KeyValueMap(Map<String, Object> sourceMap) {
			this(sourceMap, true);
		}

		KeyValueMap(Map<String, Object> sourceMap, boolean nullable) {
			final Map<String, Object> sMap = sourceMap.entrySet().stream()
					.filter((e) -> nullable || e.getValue() != null)
					.collect(HashMap::new, (m, v) -> m.put(v.getKey(), v.getValue()), HashMap::putAll);
			this.sourceMap = Collections.unmodifiableMap(sMap);
			final List<String> keysTmp = Lists.newArrayList();
			final List<Object> valuesTmp = Lists.newArrayList();
			this.sourceMap.forEach((k, v) -> {
				keysTmp.add(k);
				valuesTmp.add(v);
			});
			keys = Collections.unmodifiableList(keysTmp);
			values = Collections.unmodifiableList(valuesTmp);
		}

		public Map<String, Object> getSourceMap() {
			return sourceMap;
		}

	@Override
	public String toString() {
		return "KeyValueMap{" +
				"sourceMap=" + sourceMap +
				'}';
	}

	@Override
	public boolean equals(Object o) {
		if (this == o) return true;
		if (o == null || getClass() != o.getClass()) return false;
		KeyValueMap that = (KeyValueMap) o;
		return Objects.equals(getSourceMap(), that.getSourceMap());
	}

	@Override
	public int hashCode() {

		return Objects.hash(getSourceMap());
	}

	public List<String> getKeysAsList() {
			return keys;
		}

		public List<Object> getValuesAsList() {
			return values;
		}

		public KeyValueMap minus(@Nullable KeyValueMap otherKeyValueMap, boolean nullable) {
			if (otherKeyValueMap == null) {
				return new KeyValueMap(getSourceMap(), nullable);
			}
			final Map<String, Object> resultMap = new HashMap<>(getSourceMap());
			resultMap.entrySet().removeAll(otherKeyValueMap.getSourceMap().entrySet());
			return new KeyValueMap(resultMap, nullable);
		}

	}
}
